﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Company.IO.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Company.IO.Tests
{
    [TestClass]
    public class ReflectionTests
    {
        public TestContext TestContext { get; set; }

        [TestMethod]
        [Ignore]
        public void Reflection_Interface_createFiles()
        {
            var methods = new List<string>();
            Type interfaceType = typeof (IPath);
            Type[] exportedTypes = Assembly.GetAssembly(typeof (IFileSystem)).GetExportedTypes();
            foreach (Type exportedType in exportedTypes)
            {
                if (!exportedType.IsInterface)
                    continue;

                string name = exportedType.Name.Substring(1);
                string claasName = string.Format("{0}Test", name);
                string path = string.Format("C:\\Company\\{0}.cs", claasName);

                var codeAttributeDeclaration = new CodeAttributeDeclaration("TestClass");
                CreateTestFile(exportedType.Name, claasName, path, codeAttributeDeclaration);

                foreach (MethodInfo methodInfo in interfaceType.GetMethods())
                {
                    string methodName = methodInfo.Name;
                    string methodPath = string.Format("C:\\Company\\{0}.{1}.cs", claasName, methodName);

                    CreateTestFile(exportedType.Name, claasName, methodPath, null);

                    if (!methods.Contains(methodInfo.Name))
                    {
                        methods.Add(methodInfo.Name);
                    }
                }
            }
        }

        private static void CreateTestFile(string interfaceName, string claasName, string path, CodeAttributeDeclaration attributeDeclaration)
        {
            CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("CSharp");
            ICodeGenerator codeGenerator = codeDomProvider.CreateGenerator(path);
            string sub = string.Format("TestBase<{0}>", interfaceName);
            

            CodeCompileUnit buildHelloWorldGraph = BuildTestClaasDefinition(sub, claasName, attributeDeclaration);

            GenerateCode(codeDomProvider, buildHelloWorldGraph, path);
        }

        private static void GenerateCode(CodeDomProvider provider, CodeCompileUnit compileunit, string path)
        {
            // Build the source file name with the appropriate
            // language extension.
            String sourceFile = path;

            // Create an IndentedTextWriter, constructed with
            // a StreamWriter to the source file.
            var tw = new IndentedTextWriter(new StreamWriter(sourceFile, false), "    ");
            // Generate source code using the code generator.
            provider.GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions());
            // Close the output file.
            tw.Close();
        }


        private static CodeCompileUnit BuildTestClaasDefinition(string interfaceName, string claasName, CodeAttributeDeclaration attributeDeclaration)
        {
            // Create a new CodeCompileUnit to contain 
            // the program graph.
            var compileUnit = new CodeCompileUnit();

            // Declare a new namespace called Samples.
            var samples = new CodeNamespace("Company.System.IO.IntegrationTests");
            // Add the new namespace to the compile unit.
            compileUnit.Namespaces.Add(samples);

            // Add the new namespace import for the System namespace.
            samples.Imports.Add(new CodeNamespaceImport("Company.IO.Interfaces"));
            samples.Imports.Add(new CodeNamespaceImport("Company.System.IO.IntegrationTests.Properties"));
            samples.Imports.Add(new CodeNamespaceImport("Microsoft.VisualStudio.TestTools.UnitTesting"));

            // Declare a new type called Class1.
            var class1 = new CodeTypeDeclaration(claasName);
            class1.BaseTypes.Add(interfaceName);

            class1.IsPartial = true;

            // Declare a new code attribute
            if(attributeDeclaration != null)
                class1.CustomAttributes.Add(attributeDeclaration);

            // Add the new type to the namespace type collection.
            samples.Types.Add(class1);

            return compileUnit;
        }
    }
}